package com.google.code.sysarch.v1.e33;

import java.util.Vector;
import com.google.code.sysarch.common.util.Parameter;

public class Scheduler extends Thread {

	public static final int MIN_TIMESLICE = 10;
	protected Vector<iThread> queueBuffer;

	/* scheduler runs the workers */
	Scheduler(Parameter p, v1_33 GUI) {
		iThread.myScheduler = this;
		int numThreads = p.getInt("n", v1_33.NUM_MAX_THREADS/2, 1, v1_33.NUM_MAX_THREADS);
		this.queueBuffer = new Vector<iThread>(numThreads);
		int default_timeslice = p.getInt("t", 3, 1, 100);
		int default_work = p.getInt("w", 1000, 10, 50000);
		int isInteractive = p.getInt("p", 50, 0, 100);		
		
		for (int i=0; i<numThreads; i++) {
			int work = p.getInt("w"+i, default_work, 10, 50000);
			iThread thread = new iThread(work);
			thread.setNumber(i);
			thread.setPriority(Thread.MIN_PRIORITY);
			thread.setProgressBar(GUI.getBar());
			if (Math.random()*100 < isInteractive) {
				thread.defineAsInteractive();
				int timeslice = p.getInt("t"+i, default_timeslice, 1, 100);	// this is to prefer interactive threads 
				thread.setTimeslice(2*timeslice*MIN_TIMESLICE);
			} else {
				int timeslice = p.getInt("t"+i, default_timeslice, 1, 100);
				thread.setTimeslice(timeslice*MIN_TIMESLICE);
			}
			thread.start();
		}
	}
	
	/* scheduler wakes up one thread, then sleeps for the duration of its timeslice */
	public void run() {
		iThread runningThread = null;
		
		while (true) {	// this is our main loop
			while (this.queueBuffer.isEmpty()) {
				yield();
			}
			runningThread = getNextThreadFromQueue();
			synchronized (runningThread) {
				runningThread.setRunning(true);				
				runningThread.notify();
			}
			try {
				sleep(runningThread.getTimeslice());
			} catch (InterruptedException e) { }
			runningThread.setRunning(false);
			
			// wait until running thread is sleeping
			synchronized(runningThread) { }
		}
	}
	
	/* returns the thread index of the thread to run next */
	public iThread getNextThreadFromQueue() throws ArrayIndexOutOfBoundsException {
		return dequeue();
	}
	
	/* adds a thread to the end of the queue */
	public void queue(iThread thread) {
		if (!this.queueBuffer.contains(thread)) {
			this.queueBuffer.addElement(thread);
		}
	}
	
	/* removes a thread from the beginning of the queue and returns it */
	public iThread dequeue() throws ArrayIndexOutOfBoundsException {
		return this.queueBuffer.remove(0);
	}

}